(function (global, factory) {
  /**
   * 匹配函数，es6 && CommonJS && requireJs 规范
   */
  typeof exports === 'object' && typeof module !== 'undefined' ?
    module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.Vue = factory());
})(this, function () {
  // 统计vue 实例
  var uid = 0;
  // 报错函数
  var warn = function (msg) {
    console.error('[Vue Warn]' + msg)
  }

  var LIFECYCLE_HOOKS = [
    'beforeCreate',
    'created',
    'beforeMount',
    'mounted',
    'beforeUpdate',
    'updated',
    'beforeDestroy',
    'destroyed',
    'activated', // 内置组件 激活keep-alive
    'deactivated', // 内置组件 停用keep-alive
    'errorCaptured'
  ];
  // 选项components directives filters
  var ASSET_TYPES = [
    'component',
    'directive',
    'filter'
  ];

  // 数组的变异方法
  var methodsToPatch = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
  var cacheArrProto = Array.prototype;
  // 代理原型
  var shell = Object.create(cacheArrProto);

  methodsToPatch.forEach(function(method) {
    var caheMethod = cacheArrProto[method];
    def(shell, method, function() {
      var args = [];
      var len = arguments.length;
      while(len--) args[len] = arguments[len];
      var result = caheMethod.apply(this, args);
      var ob = this.__ob__;
      var inserted;
      switch(method) {
        case "push":
        case "unshift":
          inserted = args;
        break;
        case "splice":
          inserted = args.slice(2);
      }
      if(inserted) {
        // 监听新添加数据，加入响应式系统中
        ob.observeArray(inserted);
      }
      ob.dep.notify();

      return result;
    });
  })

  var arrayKeys = Object.getOwnPropertyNames(shell);

  var isObject = function(obj) {
    return obj !== null && typeof obj === "object";
  }

  var hasProto = "__proto__" in {};
  var shouldObserve = true;

  function toggleObserving(value) {
    shouldObserve = value;
  }

  // var _isServer;
  // var isServerRendering = function() {
  //   if(_isServer === undefined) {
  //   }
  // }

  function def(obj, key, val) {
    Object.defineProperty(obj, key, { // data.__ob__ = 实例对象
      value: val,
      enumerable: false, // 不可枚举
      configurable: true, // 属性可配置
    })
  }

  var noop = function () {}

  function Dep() {
    this.subs = []; // subs 存储 watch 的实例对象
  }
  Dep.prototype.addSub = function(sub) {
    this.subs.push(sub);
  }
  Dep.prototype.depend = function() {
    console.log("收集依赖")
  }
  Dep.prototype.notify = function() {
    var subs = this.subs.slice();
    for(var i = 0; i < subs.length; i++) {
      subs[i].updata(); // 数据更新的操作
    }
  }
  Dep.target = null;

  function Observer(value) {
    this.value = value;
    this.vmCount = 0;
    this.dep = new Dep(); // 回调列表 依赖
    // 标志
    def(value, "__ob__", this);
    // 处理响应式数组对象
    if(Array.isArray(value)) {
      var augment = hasProto ? protoAugment : copyAugment;
      augment(value, shell, arrayKeys);
    } else {
      this.walk(value);
    }
  }

  Observer.prototype.walk = function walk(obj) {
    var keys = Object.keys(obj);
    for(var i = 0, j = keys.length; i <j; i++) {
      defineReacive(obj, keys[i]);
    }
  }
  Observer.prototype.observeArray = function observeArray(items) {
    for(var i = 0,j = items.length; i < j; i++) {
      observe(items[i]);
    }
  }

  // 响应式系统入口， data
  function observe(value, asRootData) {
		if (!isObject(value)) {
			return;
		}
		var ob;
		if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
			ob = value.__ob__;
		} else if (
			shouldObserve &&
			(Array.isArray(value) || isPlainObject(value)) &&
			Object.isExtensible(value) && // Object.isExtensibl 一个对象是否可扩展
			!value._isVue
		) {
			ob = new Observer(value);
    }
		if (asRootData && ob) {
      ob.vmCount++;
    }

    return ob;
  }

  // 目标源 代理原型对象
  function protoAugment(target, src) {
    target.__proto__ = src;
  }

  // 单个绑定方法
  function copyAugment(target, src) {
    for(var i = 0, j = keys.length; i < j; i++) {
      var key = keys[i];
      def(target, key, src[key]);
    }
  }

  // 响应式系统的和兴 将数据对象属性转换为访问器属性 getter setter
  function defineReacive(obj, key, val, shallow) {
    var dep = new Dep(); // 收集依赖 回调列表
    var property = Object.getOwnPropertyDescriptor(obj, key);
    var getter = property && property.get;
    var setter = property && property.set;

    if((!getter || setter) && arguments.length === 2) {
      val = obj[key]; // 深度的观察
    }
    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
      get: function() { // 依赖收集
        var value = getter ? getter.call(obj) : val;
        if(Dep.target) { // 有要被收集的依赖
          dep.depend(); // 收集依赖
          if(childOb) {
            childOb.dep.depend();
          }
        }
        return value;
      },
      set: function(newValue) { // 调用依赖
        var value = getter ? getter.call(obj) : val;
        // NaN !== NaN
        if(newValue === value || (value !== value && newValue !== newValue)) {
          return;
        }
        if(setter) {
          setter.call(obj, newValue);
        } else {
          val = newValue;
        }
        childOb = !shallow && observe(val);
        dep.notify(); // 通知依赖更新
      }
    });
  }

  function isPlainObject(obj) {
    return toString.call(obj) === "[object Object]"
  }

  function assertObjectType(name, value, vm) {
    if (!isPlainObject(value)) {
      warn("选项" + name + "的值无效：必须是对象");
    }
  }

  function extend(to, _from) {
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to;
  }

  // ES6 方法返回一个布尔值，判断对象是否包含特定的自身（非继承）属性。
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var hasOwn = function (obj, key) {
    return hasOwnProperty.call(obj, key)
  }

  function resolveConstructorOptions(Con) {
    var options = Con.options;
    // 判断  Con.super  ===  Vue
    return options;
  }

  function checkComponents(options) {
    for (var key in options.components) {
      validataComponentName(key)
    }
  }

  // 检测key 是否在makeMap
  function makeMap(str, expectsLowerCase) {
    var map = {};
    var list = str.split(",");
    for (var i = 0; i < list.length; i++) {
      map[list[i]] = true;
    }

    return expectsLowerCase ? function (val) {
      return map[val.toLowerCase()];
    } : function (val) {
      return map[val];
    }
  }

  // components 规范的检测
  function checkComponents(options) {
    for (var key in options.components) {
      validataComponentsName(key);
    }
  }

  // html保留标签
  var isHTMLTag = makeMap(
    'html,body,base,head,link,meta,style,title,' +
    'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
    'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
    'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
    's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
    'embed,object,param,source,canvas,script,noscript,del,ins,' +
    'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
    'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
    'output,progress,select,textarea,' +
    'details,dialog,menu,menuitem,summary,' +
    'content,element,shadow,template,blockquote,iframe,tfoot'
  );

  // svg保留标签
  var isSVG = makeMap(
    'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
    'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
    'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
    true
  );

  // vue全局API
  Vue.options = {
    components: { // 内置组件 {扩展 自定义组件 第三方组件}._potro_
      keepAlive: {},
      transtion: {},
      transtionGroup: {}
    },
    directives: {},
    _base: Vue
  }

    
  // 配置对象
  var config = {
    // 自定义策略
    optionMergeStrategies: {}
  }
  var strats = config.optionMergeStrategies;
  strats.el = function (parent, child, vm, key) {
    if (!vm) {
      warn("选项" + key + "只能在实例中使用");
    }
    return defaultStart(parent, child);
  }

  function mergeData(to, from) {
    if (!from) {
      return to;
    }

  }

  function mergeDataorFn(parentVal, childVal, vm) {
    if (!vm) {
      if (!childVal) {
        return parentVal;
      }
      if (!parentVal) {
        return childVal;
      }
      // 合并处理 parentVal & childVal 对应的值都应该是函数
      return function mergeDataFn() {
        return mergeData(
          typeof childVal === "function" ? childVal.call(this, this) : childVal,
          typeof parentVal === "function" ? parentVal.call(this, this) : parentVal
        );
      }
    } else { // Vue实例调用
      return function mergeInstanceDataFn() {
        var instanceData = typeof childVal === "function" ? childVal.call(vm, vm) : childVal;
        var dafaultData = typeof parentVal === "function" ? parentVal.call(vm, vm) : parentVal;
        if (instanceData) {
          return mergeData(instanceData, dafaultData);
        } else {
          return dafaultData;
        }
      }
    }
  }

  // 自定义策略
  strats.data = function (parentVal, childVal, vm) {
    if (!vm) { // 子组件|子类调用
      if (childVal && typeof childVal !== "function") {
        warn("data选项的值应该为function 返回组件中每个实例");
      }
      return mergeDataorFn(parentVal, childVal); // 数据的合并
    }
    // Vue实例调用
    return mergeDataorFn(parentVal, childVal, vm);
  }

  function mergeHook(parentVal, childVal) {
    return childVal ?
      parentVal ?
      parentVal.concat(childVal) :
      Array.isArray(childVal) ? childVal : [childVal] : parentVal;
  }
  // 钩子函数自定义策略
  LIFECYCLE_HOOKS.forEach(function (hook) {
    strats[hook] = mergeHook;
  });

  function mergeAssets(parentVal, childVal, vm, key) {
    var res = Object.create(parentVal || null);
    if (childVal) {
      assertObjectType(key, childVal, vm);
      return extend(res, childVal);
    }
    return res;
  }
  // 资源选项 自定义策略
  ASSET_TYPES.forEach(function (type) {
    strats[type + "s"] = mergeAssets;
  });

  // wacth选项 自定义策略
  strats.watch = function (parentVal, childVal, vm, key) {
    if (!childVal) {
      return Object.create(parentVal || null);
    }
    assertObjectType(key, childVal, vm);
    if (!parentVal) {
      return childVal;
    }
    var res = {};
    extend(res, parentVal);
    for (var $key in childVal) {
      var parent = res[$key];
      var child = childVal[$key];
      if (parent && !Array.isArray(parent)) {
        parent = [parent];
      }
      res[$key] = parent ? parentVal.concat(childVal) : Array.isArray(child) ? child : [child];
    }

    return res;
  }

  strats.props =
  strats.methods =
  strats.computed = function (parentVal, childVal, vm) {
    if (!parentVal) {
      return childVal;
    }
    var res = Object.create(null);
    extend(res, parentVal);
    if (childVal) {
      extend(res, childVal);
    }
    return res;
  }

  // 内置标签
  var isbuiltInTag = makeMap("slot,component", true);
  var isReservedTag = function (tag) {
    return isHTMLTag(tag) || isSVG(tag);
  }

  function defaultStart(parent, child) {
    return child === undefined ? parent : child;
  }

  function validataComponentsName(key) {
    // 名称 不能使用vue内置标签, 不能使用 html || svg 属性名称
    // 规范 组件的名称必须是字母或中横线组成，且必须开头为字母
    if (/^[a-zA-Z][\w-]*&/.test(key)) {
      warn("组件的名称必须是字母或中横线组成，且必须开头为字母");
    }
    if (isbuiltInTag(key) || isReservedTag(key)) {
      warn("组件的名称不能使用vue内置标签, 不能使用 html || svg 属性名称");
    }
  }

  function validataComponentName() {
    //1：不能使用Vue内置标签 slot  component  2:  不能使用html || svg属性名称 
    //2: 规范组件的名称必须是由字母或中横线组成, 且必须是由字母开头
    if (!/^[a-zA-Z][\w-]*$/.test(key)) {
      warn("组件的名称必须是由字母或中横线组成, 且必须是由字母开头");
    }
  }

  var camelizeRE = /-(\w)/g; // 将中横线转化为驼峰命名
  function camelize(str) {
    return str.replace(camelizeRE, function (_, c) {
      return c ? c.toUpperCase() : "";
    });
  }

  function normalizeProps(options) {
    var props = options.props;
    if (!props) {
      return;
    }
    var res = {};
    var i, val, name;
    if (Array.isArray(props)) {
      i = props.length;
      while (i--) {
        val = props[i];
        if (typeof val === "string") {
          name = camelize(val);
          res[name] = {
            type: null
          }
        } else {
          warn("使用数组语法时：props的值必须为字符串");
        }
      }
    } else if (isPlainObject(props)) {
      for (var key in props) {
        val = props[key];
        name = camelize(key);
        res[name] = isPlainObject(val) ? val : {
          type: val
        };
      }
    } else {
      warn("选项props的值无效，应该为数组或者对象");
    }
    options.props = res;
  }

  function normalizeDirectives(options) {
    var dirs = options.directives;
    if (dirs) {
      for (var key in dirs) {
        var dif = dirs[key];
        if (typeof def === "function") {
          dirs[key] = {
            bind: dif,
            updata: dif
          }
        }
      }
    }
  }

  /**
   * 选项合并 一个或者多个对象合并并且生成一个新的对象
   * @param parent Vue.options
   * @param child 用户定义options
   * @param vm Vue实例
   */
  function mergeOptions(parent, child, vm) {
    // 规范的检测   components props inject  directives
    checkComponents(child);
    normalizeProps(child);
    normalizeDirectives(child);

    var options = Object.create(null);
    var key;
    for (key in parent) {
      mergeField(key);
    }
    for (key in child) {
      if (!hasOwn(parent, key)) {
        mergeField(key);
      }
    }

    // 默认策略
    function mergeField(key) {
      // 一默认为优先，以用户配置为覆盖
      var result = strats[key] || defaultStart;
      options[key] = result(parent[key], child[key], vm, key);
    }

    return options;
  }

  function isReserved(str) {
    var c = (str + '').charCodeAt(0);
    // 0x24 _     0x5f $
    return c === 0x24 || c === 0x5f
  }

  // 共享的访问器对象
  var shareProperty = {
    enumerable: true,
    configurable: true,
    get: noop,
    set: noop
  };
  // 代理
  function proxy(target, data, key) {
    shareProperty.get = function () {
      return this[data][key];
    }
    shareProperty.set = function (val) {
      this[data][key] = val;
    }
    Object.defineProperty(target, key, shareProperty);
  }

  function callHook(vm, hook) {
    var handlers = vm.$options[hook];
    if(handlers) {
      for(var i=0, j=handlers.length; i<j; ) {
        handlers[hook].call(vm);
      }
    }
  }

  function getData(data, vm) {
    return data.call(vm,vm);
  }
  function initData(vm) {
    var data = vm.$options.data;
    data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
    if(!isPlainObject(data)) {
      data = {};
      warn("data选项的值应该是object类型");
    }
    var keys = Object.keys(data);
    var methods = vm.$options.methods;
    var props = vm.$options.props;
    var i = keys.length;
    while(i--) {
      var key = keys[i];
      if(methods && hasOwn(methods, key)) {
        warn("methods：" + key + "选项已定义为data属性");
      }
      if(props && hasOwn(props, key)) {
        warn("props" + key + "选项已定义为data属性");
      } else if(!isReserved(key)) {
        proxy(vm, "_data", key);
      }
    }
    observe(data, true); // 相应式系统
  }

  var allowedGlobals = makeMap(
    'Infinity,undefined,NaN,isFinite,isNaN,' +
    'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
    'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
    'require' // for Webpack/Browserify
  );

  function warnNonPresent(target, key) {
    warn("属性或方法" + key + "未在实例对象上定义 渲染功能正在尝试访问这个不存在的属性," + target);
  }

  function isNative(Ctor) {
    return typeof Ctor === "function" && /native code/.test(Ctor.toString())
  }
  var hasProxy = typeof Proxy !== "undefined" && isNative(Proxy);
  var hasHandler = {
    // in 检测代理对象属性, 代理对象with 语句
    has: function (target, key) {
      var has = key in target;
			//key 是否是全局对象   渲染函数 内置方法 _
			var isAllowed = allowedGlobals(key) || (typeof key === "string" && key.charAt(0) === "_");
			if (!has && !isAllowed) { //has 
				warnNonPresent(target, key);
      }
      return has; 
    }
  };
  var getHandler = {
    get: function (target, key) {
      if(typeof key === "string" && !(key in target)) {
        warnNonPresent(target, key);
      }
      return target[key];
    }
  };

  function initProxy(vm) {
    // es6 proxy 检测 hasProxy
    if (hasProxy) {
      var options = vm.$options;
      // render渲染函数 Handlers拦截炒作
      var Handlers = options.render && options.render._withShtripped ?
        getHandler :
        hasHandler;
      vm._renderProxy = new Proxy(vm, Handlers);
    } else {
      vm._renderProxy = vm;
    }
  }

  function initLifecycle(vm) {
    var options = vm.$options;
    // 父实例的引用 父组件
    var parent = options.parent;
    // 抽象组件 1、不会出现子父级的路径上 2、不会参与dom的渲染
    // 获取父组件 当前的组件不是抽象组件
    if(parent && !options.abstract) {
      // 第一个非抽象组件的父组件
      while(parent.$options.abstract && parent.$parent) {
        parent = parent.$parent;
      }
      parent.$children.push(vm);
    }
    vm.$parent = parent;   // $parent 的值指向父级
		//设置$root  
		vm.$root = parent ? parent.$root : vm;

		vm.$children = [];   //当前实例的子组件实例数组
		vm.$refs = {};

		vm._watcher = null;
		vm._inactive = null;
		vm._directInactive = false;   
		vm._isMounted = false;  //是否挂载
		vm._isDestroyed = false; //是否销毁
		vm._isBeingDestroyed = false;   //是否正在销毁
  }

  function initState(vm) {
    var opts = vm.$options;
    if(opts.props) {
      initProps(vm, opts.props);
    }
    if(opts.methods) {
      initMethods(vm, opts.methods);
    }
    if(opts.computed) {
      initComputed(vm, opts.computed);
    }
    if(opts.data) {
      initData(vm);
    } else {
      observe(vm._data = {}, true);
    }
  }

  function initMixin(Vue) {
    Vue.prototype._init = function (options) {
      var vm = this;
      // 统计有多少vue创建
      vm._uid = uid++;
      vm._isVue = true;
      // 选项合并 Vue.options 传入的options对象合并
      // vm.constructor 指向vm的构造函数
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
      // 渲染函数的作用域代理
      initProxy(vm);
      // 将当前实例添加到父实例的$children 属性当中，并设置自身的$parent 属性指向父实例
      initLifecycle(vm);
      callHook(vm, "beforeCreate");
      // 数据初始化
      initState(vm);
      if(this.$options.el) {
        // 挂载组件
        vm.$mount(this.$options.el);
      }
    }
  }

  function Vue(options) {
    // 安全机制
    if (!(this instanceof Vue)) {
      warn('Vue是构造函数 因该使用new关键字调用');
    }
    this._init(options);
  }
  initMixin(Vue); // 初始化选项： 1.规范 2.合并

  // watcher
  function mountComponent() {
    // 组件挂载时
    // 1.render 返回生产虚拟节点
    // 2._updata 渲染函数生成的虚拟节点，渲染为DOM
    var updateComponet = function() {

    }

    // 渲染函数的观察者对象
    // updateComponet 观察目标
    new Watcher(vm, updateComponet, noop, {}, true);
  }

  // runtime $mount 运行时构建（不包含编译器） 独立构建
  Vue.prototype.$mount = function(el) {
    el = el && query(el);
    // 挂载组件 把渲染函数生成虚拟DOM 渲染成真正的DOM
    return mountComponent(this, el);
  }

  function query(el) {
    if(typeof el === "string") {
      var element = document.querySelector(el);
      if(!element) {
        warn("cannot find element" + el);
      }
      return element;
    } else {
      return el;
    }
  }

  function idToTemplate(id) {
    var el = query(id);
    return el && el.innerHTML;
  }

  function getOuterHTML(el) {
    if(el.outerHTML) {
      return el.outerHTML;
    } else {
      var con = document.createElement("div");
      con.appendChild(el.cloneNode(true));
      return con.innerHTML;
    }
  }

  // 缓存运行时 $mount
  var mount = Vue.prototype.$mount;
  // 完整版 添加了模板编译功能
  // 1：使用template选项 或者 el挂载的DOM节点作为模板编译
  // 2：模板编译成渲染函数
  Vue.prototype.$mount = function(el) {
    el = el && query(el);
    if(el === document.body || el === document.documentElement) {
      warn("禁止将组件挂载在 body || html 元素上");
    }
    var options = this.$options;
    if(!options.render) {
      var template = options.template;
      // 自定义渲染函数 模板渲染为渲染函数
      if(template) {
        if(typeof template === "string") {
          if(template.charAt(0) === "#") {
            template = idToTemplate(template); // innerHTML
          }
        }
      } else if(el) {
        template = getOuterHTML(el);
      }
    }

    if(template) {
      // 解析成渲染函数 编译 AST 虚拟DOM
      var res = compileToFunctions(template, this);
      options.render = res.render;
    }

    return mount.call(this, el);
  }

  // 初始化全局配置
  function initExtend(Vue) {
    Vue.extend = function (extendOption) { // 参数对象
      var Super = this;
      var Sub = function VueComponent(options) { // 构造函数
        this._init(options); // Vue.prototype._init()

      }
      Sub.prototype = Object.create(Super.prototype);
      Sub.prototype.constructor = Sub;
      Sub.options = mergeOptions(Super.options, extendOption || {});
      Sub.extend = Super.extend;

      return Sub;
    }
  }
  initExtend(Vue);
  return Vue
});